package com.trippin.DB.entities;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
//import java.util.SimpleTimeZone;
import java.util.TreeSet;

import javax.jdo.annotations.Column;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.NotPersistent;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.beoui.geocell.GeocellManager;
import com.beoui.geocell.annotations.Geocells;
import com.beoui.geocell.annotations.Latitude;
import com.beoui.geocell.annotations.Longitude;
import com.beoui.geocell.model.Point;
import com.google.appengine.api.datastore.Key;
import com.google.gson.annotations.Expose;
import com.trippin.Utils.CommentComparator;
import com.trippin.Utils.ScheduledAttractionEntityComparator;

@PersistenceCapable
public class AttractionEntity implements TrippinEntity, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Key key;

	@Persistent
	@Expose
	private long id;

	@Persistent
	@Longitude
	@Expose
	private double longitude;

	@Persistent
	@Latitude
	@Column
	@Expose
	private double latitude;

	@Persistent
	@Geocells
	private List<String> geoCellsData;

	@NotPersistent
	@Expose	
	private  Set<ScheduledAttractionEntity> futureScheduledAttractions;

	@NotPersistent
	@Expose	
	private  Set<ScheduledAttractionEntity> pastScheduledAttractions;

	@NotPersistent
	private  Set<ScheduledAttractionEntity> scheduleAttractions;
	@Persistent
	private  Set<Key> scheduleAttractionsKeys = new HashSet<Key>();

	/*
	@Persistent
	@Expose
	private SimpleTimeZone timeZone;
	*/
	
	@Persistent
	@Expose
	private Set<Long> pastAttendees = new HashSet<Long>();

	@Persistent
	@Expose
	private Set<Long> futureAttendees = new HashSet<Long>();
	
	@Persistent
	@Expose
	private Set<Long> notScheduledYetAttendees = new HashSet<Long>();

	@Persistent
	@Expose
	private String title = "";

	@Persistent
	@Expose
	private String description = "";
	
	@Persistent
	@Expose
	private String type = "";

	@Persistent
	@Expose
	private String address = "";

	@Persistent
	@Expose
	private String primaryPhoneNumber = "";

	@Persistent
	@Expose
	private long createdBy;

	@Persistent
	@Expose
	private Date createdOn;

	@Persistent
	@Expose
	private Date lastModifiedOn;

	@Persistent
	@Expose
	private String website = "";

	@Persistent
	@Expose
	private String email = "";

	@Persistent
	@Expose
	private Duration duration;
	
	@Persistent
	private Set<Long> attendeesPluses = new HashSet<Long>();
	
	@Persistent
	@Expose
	private long pluses;
	
	@Persistent
	private Set<Long> attendeesMinuses = new HashSet<Long>();

	@Persistent
	@Expose
	private long minuses;
	
	@Persistent
	@Expose
	private List<Link> links = new ArrayList<Link>();
	
	@Persistent
	@Expose
	private Set<Comment> comments = new TreeSet<Comment>(new CommentComparator());


	public void initNewAttraction(long createdBy, double longitude, double latitude) {
		this.createdBy = createdBy;
		this.longitude = longitude;
		this.latitude = latitude;
		this.createdOn = new Date();
		this.lastModifiedOn = this.createdOn;
		this.id = -1;
		this.pluses = 0;
		this.minuses = 0;
		this.duration = null;
		setGeoCellsData();
	}
	
	public Key getKey() {
		return key;
	}

	public void setGeoCellsData() {
		Point p = new Point();
		p.setLat(this.latitude);
		p.setLon(this.longitude);
		this.geoCellsData = GeocellManager.generateGeoCell(p);
	}

	public long getId() {
		if(id < 0)
			id = key.getId();
		return id;
	}


	public void setId() {
		id = key.getId();
	}

	public void setLongitude(double longitude) {
		this.longitude = longitude;
	}

	public double getLongitude() {
		return longitude;
	}

	public void setLatitude(double latitude) {
		this.latitude = latitude;
	}

	public double getLatitude() {
		return latitude;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getTitle() {
		return title;
	}

	public String getType() {
		return type;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getDescription() {
		return description;
	}

	/*
	public SimpleTimeZone getTimeZone() {
		return timeZone;
	}

	public void setTimeZone(SimpleTimeZone timeZone) {
		this.timeZone = timeZone;
	}
	*/

	public void setAddress(String address) {
		this.address = address;
	}

	public String getAddress() {
		return address;
	}

	public void setPrimaryPhoneNumber(String primaryPhoneNumber) {
		this.primaryPhoneNumber = primaryPhoneNumber;
	}

	public String getPrimaryPhoneNumber() {
		return primaryPhoneNumber;
	}

	public void setWebsite(String website) {
		this.website = website;
	}

	public String getWebsite() {
		return website;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getEmail() {
		return email;
	}

	public Duration getDuration() {
		return duration;
	}

	public Set<Key> getScheduledAttractionsKeys() {
		return scheduleAttractionsKeys;
	}

	public void addScheduledAttractionKey(Key scheduledAttractionsKey) {
		this.scheduleAttractionsKeys.add(scheduledAttractionsKey);
	}
	
	public void removeScheduleAttractionKey(Key scheduledAttractionsKey) {
		this.scheduleAttractionsKeys.remove(scheduledAttractionsKey);
	}

	public void setScheduleAttractions(Set<ScheduledAttractionEntity> scheduledAttractions) {
		this.scheduleAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator());
		this.scheduleAttractions.addAll(scheduledAttractions);
		arrangePastFuture();
	}
	
	public void addcheduledAttraction(ScheduledAttractionEntity scheduledAttraction) {
		 checkNulls();
		this.scheduleAttractions.add(scheduledAttraction);
	}

	public Set<ScheduledAttractionEntity> getScheduledAttractions() {
		checkNulls();
		return this.scheduleAttractions;
	}

	public Set<ScheduledAttractionEntity> getFutureScheduledAttractions() {
		checkNulls();
		return futureScheduledAttractions;
	}

	public Set<ScheduledAttractionEntity> getPasteScheduledAttractions() {
		checkNulls();
		return pastScheduledAttractions;
	}


	public long getCreaterBy() {
		return createdBy;
	}

	public void setCreatedBy(long createrBy) {
		this.createdBy = createrBy;
	}


	public Date getcreatedOn() {
		return createdOn;
	}

	public void setcreatedOn(Date createdOn) {
		this.createdOn = createdOn;
	}

	public Set<Long> getPastAttendees() {
		return pastAttendees;
	}

	public Set<Long> getFutureAttendees() {
		return futureAttendees;
	}

	public void addFutureAttendee(long userId) {
		futureAttendees.add(userId);
	}

	public void removeFutureAttendee(long userId) {
		futureAttendees.remove((Long)userId);
	}
	
	public void removeNotScheduledYetAttendee(long userId) {
		notScheduledYetAttendees.remove((Long)userId);
	}
	
	public Set<Long> getNotScheduledYetAttendees() {
		return notScheduledYetAttendees;
	}


	public void addNotScheduledYetAttendee(long userId) {
		notScheduledYetAttendees.add((Long)userId);
	}

	public List<String> getGeoCellsData() {
		return geoCellsData;
	}

	public Date getLastModifiedOn() {
		return lastModifiedOn;
	}

	public void setLastModifiedOn(Date lastModifiedOn) {
		this.lastModifiedOn = lastModifiedOn;
	}

	public long getPluses() {
		return pluses;
	}


	public void addPlus(long userId) {
		if(!attendeesPluses.contains(userId)) {
			if(attendeesMinuses.contains(userId)) {
				--this.minuses;
				attendeesMinuses.remove(userId);
			}
			else {
				++this.pluses;
				attendeesPluses.add(userId);
			}
		}
	}

	public Set<Long> getAttendeesPluses() {
		return attendeesPluses;
	}

	public long getMinuses() {
		return minuses;
	}


	public void addMinus(long userId) {
		if(!attendeesMinuses.contains(userId)) {
			if(attendeesPluses.contains(userId)) {
				--this.pluses;
				attendeesPluses.remove(userId);
			}
			else {
				++this.minuses;
				attendeesMinuses.add(userId);
			}
		}
	}
		
	public Set<Long> getAttendeesMinuses() {
		return attendeesMinuses;
	}

	public List<Link> getLinks() {
		return links;
	}

	public void addLink(Link link) {
		this.links.add(link);
	}
	
	public Set<Comment> getComments() {
		return comments;
	}
	


	public Comment getComment(long commentId) {
		for(Comment comment : comments) {
			if(comment.getId() == commentId) {
				return comment;
			}
		}
		return null;
	}

	public void addComment(Comment comment) {
		this.comments.add(comment);
	}
	
	public void removComment(Comment comment) {
		comments.remove(comment);
	}


	public void arrangePastFuture() {
		Date today = new Date();

		futureScheduledAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator());
		pastScheduledAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator());

		if(scheduleAttractions != null) {
			for(ScheduledAttractionEntity sae : scheduleAttractions) {
				if(today.after(sae.getEndTime())) {
					pastScheduledAttractions.add(sae);

					futureAttendees.removeAll(sae.getAttendees());
					pastAttendees.addAll(sae.getAttendees());
				}
				else {
					futureScheduledAttractions.add(sae);
				}
			}
		}
	}

	public void updateAttraction(AttractionEntity attraction) {
		this.title = (attraction.getTitle() != null ? attraction.getTitle() : "");
		this.description = (attraction.getDescription() != null ? attraction.getDescription() : "");
		this.email = (attraction.getEmail() != null ? attraction.getEmail() : "");
		this.address = (attraction.getAddress() != null ? attraction.getAddress() : "");
		this.website = (attraction.getWebsite() != null ? attraction.getWebsite() : "");
		this.type = (attraction.getType() != null ? attraction.getType() : "");
		this.links = (attraction.getLinks() != null ? attraction.getLinks() : new ArrayList<Link>());
		this.duration = (attraction.getDuration() != null ? new Duration(attraction.getDuration().getHours(), attraction.getDuration().getMinutes()) : null);
		this.lastModifiedOn = new Date();
	}
	
	public boolean isUserAttendMore(long scheduleAttrationId, long userId) {
		for(ScheduledAttractionEntity sae : futureScheduledAttractions) {
			if((sae.getId() != scheduleAttrationId) && (sae.getAttendees().contains(userId))) {
				return true;
			}
		}
		return false;
	}
	
	public boolean didUserAttend(long userId) {
		return pastAttendees.contains(userId);
	}
	
	public boolean isUserGoingToAttend(long userId) {
		return futureAttendees.contains(userId);
	}
	
	public Key getScheduledAttraction(long scheduleAttrId) {
		for(Key key : scheduleAttractionsKeys) {
			if(key.getId() == scheduleAttrId) {
				return key;
			}
		}
		return null;
	}
	
	public void checkNulls() {		
		if(scheduleAttractions == null) {
			this.scheduleAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator());
		}

		if(futureScheduledAttractions == null) {
			this.futureScheduledAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator());
		}
		
		if(pastScheduledAttractions == null) {
			this.pastScheduledAttractions = new TreeSet<ScheduledAttractionEntity>(new ScheduledAttractionEntityComparator()); 
		}
	}
	
	@Override
	public boolean equals(Object obj) {
		if(this == obj)
			return true;
		if(!(obj instanceof AttractionEntity))
			return false;
		
		AttractionEntity ae = (AttractionEntity)obj;
		if(ae.getId() != this.getId()) {
			return false;
		}
		return true;
	}

	
	public static class ShortDetailedAttraction implements TrippinEntity, Serializable {

		private static final long serialVersionUID = 1L;

		@Expose
		private Long id;

		@Expose
		private double longitude;

		@Expose 
		private double latitude;

		@Expose
		private String title;
		
		@Expose
		private String type;
		
		@Expose
		private String description;

		@Expose
		private Set<Long> pastAttendees;

		@Expose
		private Set<Long> futureAttendees;
		
		@Expose
		private Set<Long> notScheduledYetAttendees;

		@Expose
		private long pluses;

		@Expose
		private long minuses;

		public ShortDetailedAttraction(AttractionEntity att) {
			this.id = att.getId();
			this.longitude = att.getLongitude();
			this.latitude = att.getLatitude();
			this.title = att.getTitle();
			this.type = att.getType();
			this.pluses = att.getPluses();
			this.minuses = att.getMinuses();
			this.description = att.getDescription();
			this.pastAttendees = att.getPastAttendees();
			this.futureAttendees = att.getFutureAttendees();
			this.notScheduledYetAttendees = att.getNotScheduledYetAttendees();
		}

		public Long getId() {
			return id;
		}

		public double getLongitude() {
			return longitude;
		}

		public double getLatitude() {
			return latitude;
		}

		public Set<Long> getPastAttendees() {
			return pastAttendees;
		}

		public Set<Long> getFutureAttendees() {
			return futureAttendees;
		}

		public String getTitle() {
			return title;
		}
		
		public String getType() {
			return type;
		}

		public String getDescription() {
			return description;
		}

		public long getPluses() {
			return pluses;
		}

		public long getMinuses() {
			return minuses;
		}

		public Set<Long> getNotScheduledYetAttendees() {
			return notScheduledYetAttendees;
		}
		
		public boolean isUserAttend(long userId) {
			return (futureAttendees.contains(userId) || notScheduledYetAttendees.contains(userId));
		}

	}


}
